// https://blog.csdn.net/minger1202/article/details/52469056
/*
gcc -m32 -o demo sdlvideo_play_ok.c -lavutil -lavformat -lavcodec -lavutil -lswscale -lswresample -lSDL2 -lm -lpthread -lz `pkg-config --cflags --libs sdl2`
*/
//可以将视频播放出来，共174行代码

//
//  main.cpp
//  ffmpeg03
//
//  Created by 史明 on 16/11/3.
//  sminger1202@gmail.com
//  gcc main.c -lavformat -lavcodec `pkg-config --cflags --libs sdl2`  -o main.out
//  compile on mac OS X EI capitan 10.11.6 as following.
//  gcc main.cpp  `pkg-config --cflags --libs  libavcodec libavformat  sdl2` -o out.out
//  Copyright © 2016年 史明. All rights reserved.
//

#include <stdio.h>

#define __STDC_CONSTANT_MACROS

#ifdef _WIN32
//Windows
extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "libavutil/imgutils.h"
#include "SDL2/SDL.h"
};
#else
//Linux...
#ifdef __cplusplus
extern "C"
{
#endif
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libavutil/imgutils.h>
#include <SDL2/SDL.h>
#ifdef __cplusplus
};
#endif
#endif

//const char path[] = "/Users/shiming/ffmpegResearch/FFmpeg-Tutorial/ss1-part.mp4";
//const char path[] = "/Users/shiming/Downloads/independent.rmvb";
const char path[] = "/home/jim/video_files/s320.mp4";
int main(int argc, const char * argv[]) {
    // insert code here...
    int errCode = -1;
    int videoStreamID = -1;
    AVFormatContext *pAVFmtCtx;
    AVCodecContext *pAVCodecCtx;
    AVCodec *pAVCodec;
    AVCodecParameters *pAVCodecPars;
    AVFrame *pAVFrame;
    AVPacket *pAVPacket;
    //SDL 相关

    int screen_w,screen_h;
    SDL_Window *screen;
    SDL_Renderer* sdlRenderer;
    SDL_Texture* sdlTexture;


    //std::cout << "Hello, World!\n";
    //注册编解码器和一些其他的东西
    av_register_all();
    //打开流媒体
    if((errCode = avformat_open_input(&pAVFmtCtx, path, NULL, NULL) < 0)) {
        printf("open input file fail! err code :%d", errCode);
        return errCode;
    }

    //获取流媒体信息
    if((errCode = avformat_find_stream_info(pAVFmtCtx, NULL)) < 0) {
        printf("find input file info fail! err code :%d", errCode);
        return errCode;
    }

    //dump the info of media file onto standard err
    av_dump_format(pAVFmtCtx, 0, path, 0);


    for (int i = 0; i < pAVFmtCtx->nb_streams; ++i) {
        if (AVMEDIA_TYPE_VIDEO == pAVFmtCtx->streams[i]->codecpar->codec_type) {
            printf("video stream id : %d\n", i);
            videoStreamID = i;
        }
    }
    if (videoStreamID == -1) {
        printf("find stream id failed!");
        return -1;
    }
    pAVCodecPars = pAVFmtCtx->streams[videoStreamID]->codecpar;

    //寻找解码器，并申请解码器的上下文
    pAVCodec = avcodec_find_decoder(pAVCodecPars->codec_id);
    pAVCodecCtx = avcodec_alloc_context3(pAVCodec);
    if((errCode = avcodec_parameters_to_context(pAVCodecCtx, pAVCodecPars)) < 0) {
        printf("copy the codec parameters to context fail, err code : %d\n", errCode);
        return errCode;
    }
    //打开解码器，如果要使用av_send_frame和av_receive_frame就必须才用这种方式打开
    if((errCode = avcodec_open2(pAVCodecCtx, pAVCodec, NULL)) < 0){
        printf("open codec failed ! errcode : %d", errCode);
        return errCode;
    }

    if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) {
        printf("init sdl failed! -%s\n", SDL_GetError());
        return -1;
    }

    screen_h = pAVCodecPars->height;
    screen_w = pAVCodecPars->width;
    screen = SDL_CreateWindow("Player demo window", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, screen_w, screen_h, SDL_WINDOW_OPENGL);
    if (!screen) {
        printf("create window failed %s", SDL_GetError());
    }
    sdlRenderer = SDL_CreateRenderer(screen, -1, 0);
    sdlTexture = SDL_CreateTexture(sdlRenderer, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING,pAVCodecPars->width,pAVCodecPars->height);

    pAVFrame = av_frame_alloc();
    pAVPacket = av_packet_alloc();
    int retCode = -1;
    while (av_read_frame(pAVFmtCtx, pAVPacket) >= 0) {
        if (pAVPacket->stream_index == videoStreamID) {
            retCode = avcodec_send_packet(pAVCodecCtx, pAVPacket);
            if(!retCode) {
                retCode = avcodec_receive_frame(pAVCodecCtx, pAVFrame);
                switch (retCode) {
                    case 0:
                        printf("decode a frame success\n");
                        SDL_UpdateYUVTexture( sdlTexture, NULL, pAVFrame->data[0], pAVFrame->linesize[0],
                                          pAVFrame->data[1], pAVFrame->linesize[1],
                                          pAVFrame->data[2], pAVFrame->linesize[2]);
                        SDL_RenderClear( sdlRenderer );
                        SDL_RenderCopy( sdlRenderer, sdlTexture, NULL, NULL);
                        SDL_RenderPresent( sdlRenderer );
                        SDL_Delay(40);

                        break;
                    case AVERROR_EOF:
                        printf("the decoder has been fully flushed,\
                               and there will be no more output frames.\n");
                        break;

                    case AVERROR(EAGAIN):
                        printf("Resource temporarily unavailable\n");
                        break;

                    case AVERROR(EINVAL):
                        printf("Invalid argument\n");
                        break;
                    default:
                        break;
                }
            }
        }
        av_packet_unref(pAVPacket);
    }
    SDL_Quit();

    av_free(pAVFrame);
    av_free(pAVPacket);
    avcodec_close(pAVCodecCtx);
    avformat_close_input(&pAVFmtCtx);
    return 0;
}
